///
/// @file RecursiveDescentFlex.cpp
/// @brief 词法分析的手动实现源文件
/// @author zenglj (zenglj@live.com)
/// @version 1.1
/// @date 2024-11-23
///
/// @copyright Copyright (c) 2024
///
/// @par 修改日志:
/// <table>
/// <tr><th>Date       <th>Version <th>Author  <th>Description
/// <tr><td>2024-11-21 <td>1.0     <td>zenglj  <td>新做
/// <tr><td>2024-11-23 <td>1.1     <td>zenglj  <td>表达式版增强
/// </table>
///
#include <cctype>
#include <cstdio>
#include <cstring>
#include <string>

#include "RecursiveDescentParser.h"
#include "Common.h"

/// @brief 词法分析的行号信息
int64_t rd_line_no = 1;

/// @brief 词法分析的token对应的字符识别
std::string tokenValue;

/// @brief 输入源文件指针
FILE * rd_filein;

/// @brief 关键字与Token类别的数据结构
struct KeywordToken {
    std::string name;
    enum RDTokenType type;
};

/// @brief  关键字与Token对应表
static KeywordToken allKeywords[] = {
    {"int", RDTokenType::T_INT},
    {"return", RDTokenType::T_RETURN},
};

/// @brief 在标识符中检查是否时关键字，若是关键字则返回对应关键字的Token，否则返回T_ID
/// @param id 标识符
/// @return Token
static RDTokenType getKeywordToken(std::string id)
{
    // 如果在allkeywords中找到，则说明为关键字
    for (auto & keyword: allKeywords) {
        if (keyword.name == id) {
            return keyword.type;
        }
    }
    // 如果不再allkeywords中，说明是标识符
    return RDTokenType::T_ID;
}

/// @brief 词法文法，获取下一个Token
/// @return  Token，值保存在rd_lval中
int rd_flex()
{
    int c;              // 扫描的字符
    int tokenKind = -1; // Token的值

    // 忽略空白符号，主要有空格，TAB键和换行符
    while ((c = fgetc(rd_filein)) == ' ' || c == '\t' || c == '\n') {

        // TODO 请支持Linux/Windows/Mac系统的行号分析
        if (c == '\n') {
            rd_line_no++;
        }
    }

    // 文件结束符
    if (c == EOF) {
        // 返回文件结束符
        return RDTokenType::T_EOF;
    }

    // TODO 请自行实现删除源文件中的注释，含单行注释和多行注释等

    // 处理数字
    if (isdigit(c)) {
        bool isFloat = false;
        bool isHexFloat = false;
        std::string numStr;
        numStr.push_back(c);

        // 识别整数部分
        rd_lval.integer_num.lineno = rd_line_no;
        rd_lval.integer_num.val = c - '0';

        // 检查是否是十六进制数
        if (c == '0') {
            c = fgetc(rd_filein);
            if (c == 'x' || c == 'X') {
                // 十六进制数处理
                numStr.push_back(c);
                isHexFloat = true;

                // 重置整数值用于十六进制
                rd_lval.integer_num.val = 0;

                // 读取十六进制数字
                while (isxdigit(c = fgetc(rd_filein))) {
                    numStr.push_back(c);
                    int digit_val;
                    if (isdigit(c)) {
                        digit_val = c - '0';
                    } else {
                        digit_val = tolower(c) - 'a' + 10;
                    }
                    rd_lval.integer_num.val = rd_lval.integer_num.val * 16 + digit_val;
                }

                // 检查是否有小数点（十六进制浮点数）
                if (c == '.') {
                    isFloat = true;
                    numStr.push_back(c);

                    float decimal = 0.0f;
                    float factor = 1.0f / 16.0f;

                    while (isxdigit(c = fgetc(rd_filein))) {
                        numStr.push_back(c);
                        int digit_val;
                        if (isdigit(c)) {
                            digit_val = c - '0';
                        } else {
                            digit_val = tolower(c) - 'a' + 10;
                        }
                        decimal += digit_val * factor;
                        factor /= 16.0f;
                    }

                    rd_lval.float_num.val = rd_lval.integer_num.val + decimal;
                    rd_lval.float_num.lineno = rd_line_no;
                }

                // 检查是否有二进制指数标记 p 或 P（十六进制科学计数法）
                if (c == 'p' || c == 'P') {
                    isFloat = true;
                    numStr.push_back(c);

                    c = fgetc(rd_filein);
                    int expSign = 1;

                    if (c == '+') {
                        numStr.push_back(c);
                        c = fgetc(rd_filein);
                    } else if (c == '-') {
                        numStr.push_back(c);
                        expSign = -1;
                        c = fgetc(rd_filein);
                    }

                    if (!isdigit(c)) {
                        ungetc(c, rd_filein);
                        return RDTokenType::T_ERR;
                    }

                    int exponent = 0;
                    while (isdigit(c)) {
                        numStr.push_back(c);
                        exponent = exponent * 10 + (c - '0');
                        c = fgetc(rd_filein);
                    }

                    // 应用二进制指数（2^exp）
                    float baseValue = isFloat ? rd_lval.float_num.val : (float) rd_lval.integer_num.val;
                    float multiplier = 1.0f;
                    for (int i = 0; i < exponent; i++) {
                        multiplier = multiplier * (expSign > 0 ? 2.0f : 0.5f);
                    }

                    rd_lval.float_num.val = baseValue * multiplier;
                    rd_lval.float_num.lineno = rd_line_no;
                }

                // 多读的字符回退
                ungetc(c, rd_filein);

                // 存储数字的token值
                tokenValue = numStr;

                tokenKind = isFloat ? RDTokenType::T_HEX_FLOAT : RDTokenType::T_DIGIT;

            } else {
                // 不是十六进制，回退字符继续普通数字处理
                ungetc(c, rd_filein);
            }
        }

        // 如果不是十六进制，按原来的逻辑处理
        if (!isHexFloat) {
            // 最长匹配，直到非数字结束
            while (isdigit(c = fgetc(rd_filein))) {
                rd_lval.integer_num.val = rd_lval.integer_num.val * 10 + c - '0';
                numStr.push_back(c);
            }

            // 检查是否有小数点
            if (c == '.') {
                isFloat = true;
                numStr.push_back(c);

                // 处理小数部分
                float decimal = 0.0f;
                float factor = 0.1f;

                // 读取小数部分的数字
                while (isdigit(c = fgetc(rd_filein))) {
                    decimal += (c - '0') * factor;
                    factor *= 0.1f;
                    numStr.push_back(c);
                }

                // 设置浮点数值
                rd_lval.float_num.val = rd_lval.integer_num.val + decimal;
                rd_lval.float_num.lineno = rd_line_no;
            }

            // 检查是否有科学计数法标记 e 或 E
            if (c == 'e' || c == 'E') {
                isFloat = true;
                numStr.push_back(c);

                // 读取下一个字符，检查是否有符号
                c = fgetc(rd_filein);
                int expSign = 1;

                if (c == '+') {
                    numStr.push_back(c);
                    c = fgetc(rd_filein);
                } else if (c == '-') {
                    numStr.push_back(c);
                    expSign = -1;
                    c = fgetc(rd_filein);
                }

                // 必须有指数部分的数字
                if (!isdigit(c)) {
                    ungetc(c, rd_filein);
                    return RDTokenType::T_ERR;
                }

                // 读取指数部分
                int exponent = 0;
                while (isdigit(c)) {
                    numStr.push_back(c);
                    exponent = exponent * 10 + (c - '0');
                    c = fgetc(rd_filein);
                }

                // 应用指数
                float baseValue = isFloat ? rd_lval.float_num.val : (float) rd_lval.integer_num.val;
                float multiplier = 1.0f;
                for (int i = 0; i < exponent; i++) {
                    multiplier = multiplier * (expSign > 0 ? 10.0f : 0.1f);
                }

                rd_lval.float_num.val = baseValue * multiplier;
                rd_lval.float_num.lineno = rd_line_no;
            }

            // 多读的字符回退
            ungetc(c, rd_filein);

            // 存储数字的token值
            tokenValue = numStr;

            tokenKind = isFloat ? RDTokenType::T_FLOAT : RDTokenType::T_DIGIT;
        }
    } else if (c == '(') {
        // 识别字符(
        tokenKind = RDTokenType::T_L_PAREN;
        // 存储字符(
        tokenValue = "(";
    } else if (c == ')') {
        // 识别字符)
        tokenKind = RDTokenType::T_R_PAREN;
        // 存储字符)
        tokenValue = ")";
    } else if (c == '{') {
        // 识别字符{
        tokenKind = RDTokenType::T_L_BRACE;
        // 存储字符{
        tokenValue = "{";
    } else if (c == '}') {
        // 识别字符}
        tokenKind = RDTokenType::T_R_BRACE;
        // 存储字符}
        tokenValue = "}";
    } else if (c == ';') {
        // 识别字符;
        tokenKind = RDTokenType::T_SEMICOLON;
        // 存储字符;
        tokenValue = ";";
    } else if (c == '+') {
        // 识别字符+
        tokenKind = RDTokenType::T_ADD;
        // 存储字符+
        tokenValue = "+";
    } else if (c == '-') {
        // 识别字符-
        tokenKind = RDTokenType::T_SUB;
        // 存储字符-
        tokenValue = "-";
    } else if (c == '=') {
        // 识别字符=
        tokenKind = RDTokenType::T_ASSIGN;
    } else if (c == ',') {
        // 识别字符;
        tokenKind = RDTokenType::T_COMMA;
        // 存储字符,
        tokenValue = ",";
    } else if (isLetterUnderLine(c)) {
        // 识别标识符，包含关键字/保留字或自定义标识符

        // 最长匹配标识符
        std::string name;

        do {
            // 记录字符
            name.push_back(c);
            c = fgetc(rd_filein);
        } while (isLetterDigitalUnderLine(c));

        // 存储标识符
        tokenValue = name;

        // 多读的字符恢复，下次可继续读到该字符
        ungetc(c, rd_filein);

        // 检查是否是关键字，若是则返回对应的Token，否则返回T_ID
        tokenKind = getKeywordToken(name);
        if (tokenKind == RDTokenType::T_ID) {
            // 自定义标识符

            // 设置ID的值
            rd_lval.var_id.id = strdup(name.c_str());

            // 设置行号
            rd_lval.var_id.lineno = rd_line_no;
        } else if (tokenKind == RDTokenType::T_INT) {
            // int关键字

            // 设置类型与行号
            rd_lval.type.type = BasicType::TYPE_INT;
            rd_lval.type.lineno = rd_line_no;
        }
    } else {
        printf("Line(%lld): Invalid char %s\n", (long long) rd_line_no, tokenValue.c_str());
        tokenKind = RDTokenType::T_ERR;
    }

    // Token的类别
    return tokenKind;
}
